Comprehensions and Generator Expressions ======================================== A comprehension is a special bit of syntax that can appear almost anywhere an expression list is allowed. It is recognized by the keyword ``for`` in the comprehension. There are many types of comprehensions, and we'll cover each one as we cover the associated type. For now, we will only cover the generator expression, but by doing so, we'll learn the full syntax for the comprehension, which can be substituted in later. Simple Syntax ------------- The simple syntax of the comprehension is as follows: .. code:: python for in This mirrors the syntax of the ``for`` statement, except that it doesn't have a full-blown suite. Instead, the entire body of the ``for`` loop is the expression on the left hand side. What, specifically, this means depends on the context of the comprehension. I'm going to show you two contexts: Generator Expression -------------------- We have already covered :ref:`generators`. These are functions with at least one ``yield`` statement or expression in them. You can create generators on the fly if you surround the comprehension with parentheses: .. code:: python >>> g = (i**2 for i in range(10)) >>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> next(g) 16 Remember that generators are iterators, so anywhere you can use an iterable, you can use the generator expression above. Using a Comprehension in a Function Call ---------------------------------------- Many functions take an iterable as its only parameter. You can replace the parameter list with a comprehension. .. code:: python min(i%5 for i in range(1,8,3)) Note that no other parameters are allowed. If you wanted to allow other parameters, just use the generator syntax: .. code:: python sum((i**2 for i in range(10)), 8) Nesting ``for`` Blocks ------------------ You can chain ``for`` blocks in a comprehension. Keep in mind that if your comprehension gets too complicated, it will be hard to understand. It may be easier to write a full ``for`` statement. .. code:: python for in for in Typically, we use this to iterate across two indexes: .. code:: python (i,j) for i in range(3) for j in range(3) The later for blocks are embedded in the earlier ones: .. code:: python >>> g = ((i,j) for i in range(3) for j in range(3)) >>> next(g) (0, 0) >>> next(g) (0, 1) >>> next(g) (0, 2) >>> next(g) (1, 0) >>> next(g) (1, 1) >>> next(g) (1, 2) >>> next(g) (2, 0) >>> next(g) (2, 1) >>> next(g) (2, 2) >>> next(g) Traceback (most recent call last): File "", line 1, in StopIteration Nesting ``if`` Blocks ------------- After the first ``for`` block, you can have one or more ``if`` blocks. These nest much like ``for`` blocks do. .. code:: python for in if Only if the condition is ``True`` will the iteration be used. Otherwise, it is skipped. .. code:: python >>> g = (i for i in range(10) if i%5==0) >>> next(g) 0 >>> next(g) 5 >>> next(g) Traceback (most recent call last): File "", line 1, in StopIteration Combining Multiple blocks ------------------------- If you visualize comprehensions this way, you can easily see what is happening when you see a complicated comprehension. * Nest the if and for blocks according to their order. * If the condition is ``False`` (``if not cond:``) then ``continue``. Or, rather, only keep going if the condition is ``True``. * ``yield`` the ```` in the center. For example, let's write out a complicated comprehension: .. code:: python for in \ if \ for in \ for in \ if for in : if : for in : for in : if : yield Of course, if you're using more than a few blocks in your comprehension, you're probably better off re-writing it as a full-blown ``for`` loop, for the sake of legibility. Example: Matrix Indexes ----------------------- In this example, we want to generate all of the ``(i,j)`` pairs of matrix coordinates for a 3x3 matrix. .. code:: python (i,j) for i in range(3) for j in range(3) This could be used to rewrite our matrix addition routine, but using comprehensions for a ``for`` expression list is a little weird: .. code:: python for i,j in ((i,j) for i in range(3) for j in range(3)): for i in range(3): for j in range(3): Example: Simple Transformation ------------------------------ The most common way I use comprehensions is if I want to do something simple to a sequence, aka ``map()``. Let's compare these two equivalent functions: .. code:: python (x**2 for x in range(100)) map(lambda x: x**2, range(100)) Example: Simple Filtering ------------------------- We can also replace ``filter()`` with a comprehension: .. code:: python (i for i in range(100) if i%7 == 1) filter(lambda i: i%7 == 1, range(100)) Analysis -------- Comprehensions are a neat little trick in Python. It is fairly powerful, and if you are familiar with SQL, you should be getting a SQL vibe from them. However, I recommend using them sparingly, if at all. Generally, if you have occasion to use a comprehension, it is either such a simple case that you can avoid doing it at all, or it will grow to be so complex that you wouldn't want to use it at all. Also note, comprehensions are a feature of Python that isn't shared by many languages. People coming from a C/C++ or Java background will find them entirely new, and will have to read the documentation on Python to understand what they do. So it has a bit of a "Not for Noobs" vibe to them. Beginning Python programmers will be utterly confused by comprehensions and such, so if you are working on software with a team of people who aren't very good at Python yet, it's best to avoid it for the sake of efficiency. We will mention them as they arise in the Python syntax, but again, use it sparingly, if at all, and only for the simplest cases.